/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package avl.sv.client;

import avl.sv.client.source.StudySourcePort;
import avl.sv.server.training.Training;
import avl.sv.shared.Annotations.Annotation;
import avl.sv.shared.Annotations.AnnotationSet;
import avl.sv.shared.Annotations.ROI;
import avl.sv.shared.JXTreeTableROI_Model;
import avl.sv.shared.Permissions;
import avl.sv.shared.SlideReference;
import avl.sv.shared.TreeTransferHandler;
import avl.sv.shared.XML.AnnotationsXML_Parser;
import avl.sv.shared.XML.AperioAnnotationXML_Writer;
import avl.sv.shared.fileFilters.XML_Filter;
import avl.sv.shared.sources.SlideSource;
import avl.sv.shared.sources.SlideSourceFile;
import avl.sv.shared.sources.StudySource;
import com.sun.imageio.plugins.jpeg.JPEGMetadata;
import ij.gui.MessageDialog;
import java.awt.BasicStroke;
import javax.imageio.ImageWriter;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.AbstractAction;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DropMode;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.TreePath;
import javax.xml.parsers.ParserConfigurationException;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.xml.sax.SAXException;

/**
 *
 * @author benbryan
 */
public class ROI_Manager extends JPanel implements MouseMotionListener {

    private int cornerMarkerDim = 10;
    private SlideViewer slideViewer;
    private JXTreeTableROI_Model treeModelROI;
    private AnnotationSet annoSet = null;
    private Annotation lastSelecteAnnotation;
    private JFileChooser jFileChooserAnnotationExport;
    private boolean roisModified = false;
    private ExportDialogROIs exportDialog;
    private long studiesListVersion = 0;
    private final JFileChooser jFileChooserAnnotationImport;

    public ROI_Manager(SlideViewer slideViewer) {
        initComponents();
        this.slideViewer = slideViewer;
        exportDialog = new ExportDialogROIs(null, true, slideViewer.getSlideSource());
        if (slideViewer.getSlideSource() instanceof SlideSourceFile) {
            setAllComponentsEnabled(this, false);
        }

        jXTreeTableROIs.setDragEnabled(true);
        jXTreeTableROIs.setDropMode(DropMode.ON);
        jXTreeTableROIs.setTransferHandler(TreeTransferHandler.getInstance());
        jXTreeTableROIs.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

        ComboBoxModel<StudySource> modelSrc = AdvancedVirtualMicroscope.getStudyManager().getStudiesComboBoxModel();
        StudySource studySources[] = new StudySource[modelSrc.getSize()];
        for (int i = 0; i < modelSrc.getSize(); i++) {
            studySources[i] = modelSrc.getElementAt(i);
        }
        jComboBoxStudy.setModel(new DefaultComboBoxModel<>(studySources));
        jComboBoxStudy.setSelectedItem(AdvancedVirtualMicroscope.getStudyManager().getSelectedStudySource());
        setAllComponentsEnabled(this, getSelectedStudySource() != null);

        setVisible(true);
        slideViewer.addMouseMotionListener(this);
        jFileChooserAnnotationExport = new JFileChooser();
        jFileChooserAnnotationExport.setFileFilter(new XML_Filter());
        jFileChooserAnnotationExport.setAcceptAllFileFilterUsed(false);

        jFileChooserAnnotationImport = new JFileChooser();
        jFileChooserAnnotationImport.setFileFilter(new XML_Filter());
        jFileChooserAnnotationImport.setAcceptAllFileFilterUsed(false);

        setupPopupMenu();

    }

    private void setupPopupMenu() {
        jXTreeTableROIs.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    final TreePath path = jXTreeTableROIs.getPathForLocation(e.getX(), e.getY());
                    if (path == null) {
                        return;
                    }
                    JPopupMenu menu = new JPopupMenu();
                    final Object node = path.getLastPathComponent();
                    if (node instanceof ROI) {
                        JMenuItem gotoROI = new JMenuItem("Goto");
                        gotoROI.addActionListener(new AbstractAction() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                ROI roi = (ROI) node;
                                Rectangle bounds = roi.getPolygon().getBounds();
                                slideViewer.setImageRegion(bounds);
                            }
                        });
                        menu.add(gotoROI);
                    }

                    JMenuItem delete = new JMenuItem("Delete");
                    delete.addActionListener(new AbstractAction() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            int[] rows = jXTreeTableROIs.getSelectedRows();
                            Object objs[] = new Object[rows.length];
                            for (int rIdx = 0; rIdx < rows.length; rIdx++) {
                                objs[rIdx] = jXTreeTableROIs.getPathForRow(rows[rIdx]).getLastPathComponent();
                            }
                            for (Object obj : objs) {
                                if (obj instanceof Annotation) {
                                    Annotation anno = (Annotation) obj;
                                    if (anno.equals(lastSelecteAnnotation)) {
                                        lastSelecteAnnotation = null;
                                    }
                                    treeModelROI.removeNodeFromParent(anno);
                                } else if (obj instanceof ROI) {
                                    ROI roi = (ROI) obj;
                                    if (roi.getParent().getParent() != null) {
                                        treeModelROI.removeNodeFromParent(roi);
                                    }
                                }
                            }
                            roisModified = true;
                            jXTreeTableROIs.updateUI();
                            slideViewer.repaint();
                            repaint();
                        }
                    });
                    menu.add(delete);

                    JMenuItem export = new JMenuItem("Export");
                    export.addActionListener(new AbstractAction() {
                        @Override
                        public void actionPerformed(ActionEvent e) {

                            int[] rows = jXTreeTableROIs.getSelectedRows();
                            Object objs[] = new Object[rows.length];
                            for (int i = 0; i < rows.length; i++) {
                                objs[i] = jXTreeTableROIs.getPathForRow(rows[i]).getLastPathComponent();
                            }
                            exportDialog.promptForExport(objs);
                        }
                    });
                    menu.add(export);

                    if (node instanceof Annotation) {
                        final Annotation annotation = (Annotation) node;
                        final JCheckBoxMenuItem visible = new JCheckBoxMenuItem("Visible");
                        visible.setSelected(annotation.visible);
                        visible.addActionListener(new AbstractAction() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                annotation.visible = visible.isSelected();
                                slideViewer.repaint();
                            }
                        });
                        menu.add(visible);
                    }

                    menu.show(jXTreeTableROIs, e.getX(), e.getY());
                }
            }
        });
    }

    public AnnotationSet getAnnotationSet() {
        return annoSet;
    }

    public AnnotationSet getSelectedROIs() {
        AnnotationSet annoSetTemp = new AnnotationSet(slideViewer.getSlideSource().getSlideReference());
        Annotation anno = Annotation.createDefault();
        annoSetTemp.add(anno);

        int[] rows = jXTreeTableROIs.getSelectedRows();
        TreePath paths[] = new TreePath[rows.length];
        for (int rIdx = 0; rIdx < rows.length; rIdx++) {
            paths[rIdx] = jXTreeTableROIs.getPathForRow(rows[rIdx]);
        }

        if (paths == null) {
            return null;
        }
        for (TreePath path : paths) {
            for (Object obj : path.getPath()) {
                if (obj instanceof ROI) {
                    ROI roi = (ROI) obj;
                    anno.add((ROI) roi.clone());
                }
            }
        }
        if (anno.getRois().isEmpty()) {
            return null;
        } else {
            return annoSetTemp;
        }
    }

    public final void setAllComponentsEnabled(JPanel panel, boolean state) {
        Component[] comps = panel.getComponents();
        for (Component c : comps) {
            c.setEnabled(state);
            if (c instanceof JPanel) {
                setAllComponentsEnabled((JPanel) c, state);
            }
        }
        if (this.equals(panel)) {
            slideViewer.getToolPanel().setVisible(state);
            if (state == false) {
                DefaultMutableTreeTableNode msgNode = new DefaultMutableTreeTableNode();
                msgNode.add(new DefaultMutableTreeTableNode("To create annotations"));
                msgNode.add(new DefaultMutableTreeTableNode("  first create a study"));
                msgNode.add(new DefaultMutableTreeTableNode("  then reopen this image"));
                jXTreeTableROIs.setTreeTableModel(new DefaultTreeTableModel(msgNode));
            }
        }
    }

    public final void populateStudiesComboBox() {
        Training trainingPort = AdvancedVirtualMicroscope.getTrainingPort();
        if (trainingPort == null) {
            return;
        }
        long tempLong = trainingPort.getStudiesListVersion();
        if (studiesListVersion == tempLong) {
            return;
        }
        studiesListVersion = tempLong;

        String setRefs = trainingPort.getStudiesList();
        ArrayList<StudyRef> setRefList = new ArrayList<>();
        if (!setRefs.isEmpty()) {
            for (final String pair : setRefs.split(";")) {
                final String temp[] = pair.split(",");
                if (temp.length == 2) {
                    setRefList.add(new StudyRef(Integer.valueOf(temp[0]), temp[1]));
                }
            }
        }
        Collections.sort(setRefList, new Comparator<StudyRef>() {
            @Override
            public int compare(StudyRef s1, StudyRef s2) {
                return s1.name.compareTo(s2.name);
            }
        }
        );

        ArrayList<StudySource> studies = new ArrayList<>();
        for (StudyRef ref : setRefList) {
            StudySourcePort study = new StudySourcePort(trainingPort, ref.name, ref.id);
            if (study.getVersion() == -1) { //does not exist
                continue;
            }
            studies.add(study);
        }
        StudySource selectedStudy = getSelectedStudySource();
        jComboBoxStudy.setModel(new DefaultComboBoxModel<>(studies.toArray(new StudySource[studies.size()])));

        //Select the study again if it exists, and exists in the combobox
        jComboBoxStudy.setSelectedIndex(-1);
        if (selectedStudy == null) {
            if (jComboBoxStudy.getItemCount() > 0) {
                jComboBoxStudy.setSelectedIndex(0);
            } else {
            }
        } else {
            for (int i = 0; i < jComboBoxStudy.getItemCount(); i++) {
                if (jComboBoxStudy.getItemAt(i).getId() == selectedStudy.getId()) {
                    jComboBoxStudy.setSelectedIndex(i);
                    return;
                }
            }
        }
        if (jComboBoxStudy.getItemCount() > 0) {
            jComboBoxStudy.setSelectedIndex(0);
        } else {
            jComboBoxStudy.setSelectedIndex(-1);
        }
        setAllComponentsEnabled(this, getSelectedStudySource() != null);
    }

    public void addROI(ROI roi) {
        if (roi == null) {
            return;
        }
        Annotation anno = getSelectedAnnotation();
        if (anno == null) {
            if (lastSelecteAnnotation != null) {
                anno = lastSelecteAnnotation;
            } else {
                anno = getDefaultAnnotation();
            }
        }
        anno.add(roi);

        jXTreeTableROIs.updateUI();
        TreePath tp = new TreePath(new Object[]{annoSet, anno, roi});
        jXTreeTableROIs.setSelectionPath(tp);
//        AdvancedVirtualMicroscope.getSolutionManager().sets
        AdvancedVirtualMicroscope.getSolutionManager().updateButtons(slideViewer, null);
    }

    public void paintROI(Graphics2D g, ROI roi, Color color) {
        Shape s = roi.getShape();

        float zFactor = (float) (1 / g.getTransform().getScaleX());
        g.setColor(color);
        g.setStroke(new BasicStroke(5 * zFactor));
        g.draw(s);

        g.setColor(Color.BLACK);
        g.setStroke(new BasicStroke(1 * zFactor));
        g.draw(s);

        g.setStroke(new BasicStroke(3 * zFactor));

        if (roi.highlighted && jCheckBoxRegionHighlight.isSelected()) {
            g.setColor(Color.green);
            Color c = g.getColor();
            g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), 30));
            g.fill(roi.getShape());
        }
        if (jCheckBoxShowSelectedMarkers.isSelected() || jCheckBoxShowAllMarkers.isSelected()) {
            if (roi.selected && jCheckBoxShowSelectedMarkers.isSelected()) {
                g.setColor(Color.red);
            } else if (jCheckBoxShowAllMarkers.isSelected()) {
                g.setColor(Color.black);
            } else {
                return;
            }

            Polygon poly = roi.getPolygon();
            if (poly != null) {
                int dim = (int) (cornerMarkerDim / slideViewer.getMagnification());
                for (int j = 0; j < poly.npoints; j++) {
                    g.drawOval(poly.xpoints[j] - dim / 2, poly.ypoints[j] - dim / 2, dim, dim);
                }
            }
        }
    }

    public void paintROIs(Graphics gOrig) {
        if (annoSet == null) {
            return;
        }
        Graphics2D g = (Graphics2D) gOrig.create();
        slideViewer.concatenateImageToDisplayTransform(g);
        for (Annotation anno : annoSet.getAnnotations()) {
            if (!anno.visible) {
                continue;
            }
            for (ROI roi : anno.getRois()) {
                paintROI(g, roi, new Color(anno.lineColor));
            }
        }
    }

    public void setupJTreeAnnotations() {
        treeModelROI = new JXTreeTableROI_Model(annoSet);
        jXTreeTableROIs.setTreeTableModel(treeModelROI);

        treeModelROI.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                slideViewer.repaint();
            }
        });

        // Setting the treeTableModel above does not setup the colums properly
        // so this performes that function manually 
        TableColumnModel columnModel = new DefaultTableColumnModel();
        for (int i = 0; i < treeModelROI.getColumnCount(); i++) {
            TableColumn tc = new TableColumn();
            tc.setHeaderValue(treeModelROI.getColumnName(i));
            tc.setModelIndex(i);
            if (i > 0) {
                tc.setMaxWidth(50);
            }
            columnModel.addColumn(tc);
        }
        jXTreeTableROIs.setColumnModel(columnModel);

        // select already selected rois in jtree
        ArrayList<TreePath> paths = new ArrayList<>();
        for (Annotation anno : annoSet.getAnnotations()) {
            ArrayList<ROI> rois = anno.getRois();
            for (ROI roi : rois) {
                if (roi.selected) {
                    Object o[] = new Object[3];
                    o[0] = treeModelROI.getRoot();
                    o[1] = anno;
                    o[2] = roi;
                    paths.add(new TreePath(o));
                }
            }
        }
        jXTreeTableROIs.setSelectionPaths(paths);
        slideViewer.repaint();
    }

    public StudySource getSelectedStudySource() {
        return (StudySource) jComboBoxStudy.getSelectedItem();
    }

    private void populateAnnotationSet() {
        final StudySource studySource = getSelectedStudySource();
        if (studySource == null) {
            return;
        }
        final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
        Runnable task = new Runnable() {
            @Override
            public void run() {
                try {
                    SlideReference slideReference = slideViewer.getSlideSource().getSlideReference();
                    annoSet = studySource.getAnnotations(slideReference);
                    if (annoSet == null) {
                        MessageDialog md = new MessageDialog(null, "ERROR", "Failed to load annotations");
                        annoSet = new AnnotationSet(slideReference);
                    }
                    setupJTreeAnnotations();
                    roisModified = false;
                    lastSelecteAnnotation = null;

                    String ps = AdvancedVirtualMicroscope.getTrainingPort().getPermissionsStudy(studySource.getId(), null);
                    Permissions p = Permissions.valueOf(ps);
                    jButtonSaveROIs.setEnabled(p.canModify());
                    jXTreeTableROIs.setEditable(p.canModify());
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "", ex);
                }
// Current working place 
            }
        };
        worker.submit(task);
    }

    public File promptForOutputDir() {
        jFileChooser1.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        jFileChooser1.setDialogTitle("Choose output directory");
        jFileChooser1.setApproveButtonText("Select Folder");
        if (jFileChooser1.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
            return null;
        }
        return jFileChooser1.getSelectedFile();
    }

    public void exportROI(File dir, ROI roi) {
        try {
            Rectangle rec = roi.getPolygon().getBounds();
            String imageType = "jpg";
            for (int i = 0; i < 9999; i++) {
                String num = i == 0 ? "" : "_" + String.valueOf(i);
                File out = new File(dir + File.separator + roi.text + num + "." + imageType);
                if (out.exists()) {
                    continue;
                }
                if (out.getParentFile().canWrite()) {
                    BufferedImage img = slideViewer.getSlideSource().getSubImage(rec);
                    //ImageIO.write(img, imageType, out);

                    ImageOutputStream ios = ImageIO.createImageOutputStream(out);
                    Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");
                    ImageWriter writer = iter.next();
                    ImageWriteParam iwp = writer.getDefaultWriteParam();
                    iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    iwp.setCompressionQuality(1f);
                    writer.setOutput(ios);

                    JPEGMetadata data = (JPEGMetadata) writer.getDefaultImageMetadata(new ImageTypeSpecifier(img), iwp);
// This should be writing the ROI to the jpeg file, but it is not
//                    Node tree = data.getAsTree("javax_imageio_jpeg_image_1.0");                   
//                   
//                    for (Node n = tree.getFirstChild().getFirstChild(); n != null; n = n.getNextSibling()){
//                        if (n.getNodeName().equals("app0JFIF")){
//                            IIOMetadataNode jfif = (IIOMetadataNode) n;
//                            Annotation anno = new Annotation();
//                            anno.add(roi);
//                            AnnotationSet annoSet = new AnnotationSet(slideViewer.getSlideSource().getSlideReference());
//                            annoSet.add(anno);
//                            String xml = AnnotationsXML_Writer.getXMLString(annoSet.getAnnotations());
//                            
//                            jfif.setAttribute("Annotations", xml );
//                            jfif.setAttribute("Annotation_BoundingBox", 
//                                "X="        + String.valueOf(rec.x)     + "|" +  
//                                "Y="        + String.valueOf(rec.y)     + "|" + 
//                                "Width="    + String.valueOf(rec.width) + "|" + 
//                                "Height="   + String.valueOf(rec.height) );
//                        }
//                    }		

                    writer.write(data, new IIOImage(img, null, data), iwp);
                    writer.dispose();
                    ios.close();
                    return;
                }
            }

        } catch (IOException ex) {
            System.out.println("failed to get Subimage");
            System.out.println(ex);
        }
    }

    private Annotation getDefaultAnnotation() {
        if (annoSet == null) {
            Annotation anno = addNewAnnotation();
            anno.name = "default";
            return anno;
        }
        for (Annotation anno : annoSet.getAnnotations()) {
            if (anno.name.equals("default")) {
                return anno;
            }
        }
        Annotation anno = addNewAnnotation();
        anno.name = "default";
        return anno;
    }

    public Annotation getSelectedAnnotation() {
        int r = jXTreeTableROIs.getSelectedRow();
        if (r < 0) {
            return null;
        }
        TreePath treePath = jXTreeTableROIs.getPathForRow(r);

        if (treePath == null) {
            return null;
        }
        try {
            Object[] path = treePath.getPath();
            for (int i = path.length - 1; i >= 0; i--) {
                if (path[i].getClass().equals(Annotation.class)) {
                    return (Annotation) path[i];
                }
            }
            return null;
        } catch (Exception ex) {
            return null;
        }
    }

    private void highlightRegion(Point p) {
        if (annoSet == null) {
            return;
        }
        Point p2 = (Point) p.clone();
        Graphics2D g = (Graphics2D) slideViewer.getGraphics().create();
        slideViewer.concatenateImageToDisplayTransform(g);
        try {
            g.getTransform().inverseTransform(p, p2);
        } catch (NoninvertibleTransformException ex) {
            Logger.getLogger(ROI_Manager.class.getName()).log(Level.SEVERE, null, ex);
        }

        for (Annotation anno : annoSet.getAnnotations()) {
            ArrayList<ROI> rois = anno.getRois();
            for (ROI roi : rois) {
                roi.highlighted = roi.containsPoint(p2);
            }
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        if (!e.getSource().equals(slideViewer)) {
            return;
        }
        highlightRegion(e.getPoint());
        slideViewer.repaint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    public void promptToSaveROIs() {
        if (getSelectedStudySource() == null) {
            return;
        }
        if (roisModified && jButtonSaveROIs.isEnabled()) {
            Object[] options = {"Yes", "No"};
            int n = JOptionPane.showOptionDialog(this,
                    "Save annotations?",
                    "Warning",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE,
                    null,
                    options,
                    options[0]);

            if (n == JOptionPane.OK_OPTION) {
                saveAnnotations();
            }
        }
    }

    private void saveAnnotations() {
        StudySource studySource = getSelectedStudySource();
        if (studySource == null) {
            new MessageDialog(AdvancedVirtualMicroscope.getInstance(), "Error", "Select or create a study first");
            return;
        }
        jButtonSaveROIs.setEnabled(false);
        String oldText = jButtonSaveROIs.getText();
        jButtonSaveROIs.setText("Sending...");
        try {
            String result = studySource.setAnnotations(annoSet);
            if (result.equals("Success")) {
                jButtonSaveROIs.setText("Success");
                roisModified = false;
            } else {
                jButtonSaveROIs.setText("Failure");
            }
        } catch (Exception ex) {
            Logger.getLogger(ROI_Manager.class.getName()).log(Level.SEVERE, null, ex);
            jButtonSaveROIs.setText("Failure");
        }
        jButtonSaveROIs.setEnabled(true);
        Executors.newSingleThreadScheduledExecutor().schedule(new SetButtonText(oldText, jButtonSaveROIs), 5, TimeUnit.SECONDS);
    }

    private void updateSelectedROIs() {
        for (Annotation anno : annoSet.getAnnotations()) {
            ArrayList<ROI> rois = anno.getRois();
            for (ROI roi : rois) {
                roi.selected = false;
            }
        }

        int[] rows = jXTreeTableROIs.getSelectedRows();
        TreePath paths[] = new TreePath[rows.length];
        for (int rIdx = 0; rIdx < rows.length; rIdx++) {
            paths[rIdx] = jXTreeTableROIs.getPathForRow(rows[rIdx]);
        }

        if (paths == null) {
            return;
        }
        for (TreePath path : paths) {
            try {
                DefaultMutableTreeTableNode node = (DefaultMutableTreeTableNode) path.getLastPathComponent();
                if (node instanceof Annotation) {
                    Annotation anno = (Annotation) node;
                    lastSelecteAnnotation = anno;
                    ArrayList<ROI> rois = (anno).getRois();
                    for (ROI roi : rois) {
                        roi.selected = true;
                    }
                }
                if (node instanceof ROI) {
                    ROI roi = (ROI) node;
                    lastSelecteAnnotation = (Annotation) roi.getParent();
                    roi.selected = true;
                }

            } catch (Exception ex) {
            }
        }
        slideViewer.repaint();
    }

    public void setSelectionPaths(ArrayList<TreePath> paths) {
        jXTreeTableROIs.setSelectionPaths(paths);
    }

    public void setRoisModified(boolean b) {
        roisModified = b;
    }

    public void removeROI(ROI roi) {
        treeModelROI.removeNodeFromParent(roi);
        roisModified = true;
        jXTreeTableROIs.updateUI();
        slideViewer.repaint();
        repaint();
    }

    public ExportDialogROIs getExportDialogROIs() {
        return exportDialog;
    }

    private class SetButtonText implements Runnable {

        final private String str;
        final private JButton button;

        public SetButtonText(String str, JButton button) {
            this.str = str;
            this.button = button;
        }

        @Override
        public void run() {
            button.setText(str);
        }
    }

    private Annotation addNewAnnotation() {
        Annotation anno = Annotation.createDefault();
        if (annoSet == null) {
            SlideSource ss = slideViewer.getSlideSource();
            annoSet = new AnnotationSet(ss.getSlideReference());
            setupJTreeAnnotations();
        }
        annoSet.add(anno);
        jXTreeTableROIs.updateUI();
        TreePath tp = new TreePath(new Object[]{annoSet, anno});
        jXTreeTableROIs.setSelectionPath(tp);
        return anno;
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jFileChooser1 = new javax.swing.JFileChooser();
        jPanel1 = new javax.swing.JPanel();
        jPanelAddRoi = new javax.swing.JPanel();
        jButtonAddAnnotationSet = new javax.swing.JButton();
        jButtonSaveROIs = new javax.swing.JButton();
        jButtonImport = new javax.swing.JButton();
        jButtonExport = new javax.swing.JButton();
        jCheckBoxRegionHighlight = new javax.swing.JCheckBox();
        jCheckBoxShowSelectedMarkers = new javax.swing.JCheckBox();
        jCheckBoxShowAllMarkers = new javax.swing.JCheckBox();
        jScrollPane1 = new javax.swing.JScrollPane();
        jXTreeTableROIs = new avl.sv.shared.JXTreeTableROIs();
        jComboBoxStudy = new javax.swing.JComboBox<StudySource>();
        jLabel1 = new javax.swing.JLabel();

        setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 2, true));
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentShown(java.awt.event.ComponentEvent evt) {
                formComponentShown(evt);
            }
        });

        jButtonAddAnnotationSet.setText("Add Annotation");
        jButtonAddAnnotationSet.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonAddAnnotationSetActionPerformed(evt);
            }
        });

        jButtonSaveROIs.setText("Save ROIs");
        jButtonSaveROIs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSaveROIsActionPerformed(evt);
            }
        });

        jButtonImport.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
        jButtonImport.setText("Import");
        jButtonImport.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonImportActionPerformed(evt);
            }
        });

        jButtonExport.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
        jButtonExport.setText("Export");
        jButtonExport.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonExportActionPerformed(evt);
            }
        });

        jCheckBoxRegionHighlight.setSelected(true);
        jCheckBoxRegionHighlight.setText("Region Highlight");
        jCheckBoxRegionHighlight.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxRegionHighlightActionPerformed(evt);
            }
        });

        jCheckBoxShowSelectedMarkers.setSelected(true);
        jCheckBoxShowSelectedMarkers.setText("Show Selected Markers");
        jCheckBoxShowSelectedMarkers.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxShowSelectedMarkersActionPerformed(evt);
            }
        });

        jCheckBoxShowAllMarkers.setText("Show All Markers");
        jCheckBoxShowAllMarkers.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxShowAllMarkersActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanelAddRoiLayout = new javax.swing.GroupLayout(jPanelAddRoi);
        jPanelAddRoi.setLayout(jPanelAddRoiLayout);
        jPanelAddRoiLayout.setHorizontalGroup(
            jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jButtonAddAnnotationSet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jButtonSaveROIs, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                        .addComponent(jButtonImport)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonExport))
                    .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                        .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jCheckBoxShowAllMarkers)
                            .addComponent(jCheckBoxShowSelectedMarkers)
                            .addComponent(jCheckBoxRegionHighlight))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        jPanelAddRoiLayout.setVerticalGroup(
            jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jButtonAddAnnotationSet)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonSaveROIs)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButtonImport)
                    .addComponent(jButtonExport))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxRegionHighlight)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxShowSelectedMarkers)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxShowAllMarkers)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanelAddRoi, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanelAddRoi, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jXTreeTableROIs.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                jXTreeTableROIsValueChanged(evt);
            }
        });
        jScrollPane1.setViewportView(jXTreeTableROIs);

        jComboBoxStudy.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
            public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
            }
            public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) {
            }
            public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {
                jComboBoxStudyPopupMenuWillBecomeVisible(evt);
            }
        });
        jComboBoxStudy.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jComboBoxStudyActionPerformed(evt);
            }
        });

        jLabel1.setText("Selected Study");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 286, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jComboBoxStudy, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, 0))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jComboBoxStudy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 449, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void jXTreeTableROIsValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_jXTreeTableROIsValueChanged
        updateSelectedROIs();
    }//GEN-LAST:event_jXTreeTableROIsValueChanged

    private void jCheckBoxShowAllMarkersActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxShowAllMarkersActionPerformed
        slideViewer.repaint();
    }//GEN-LAST:event_jCheckBoxShowAllMarkersActionPerformed

    private void jCheckBoxShowSelectedMarkersActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxShowSelectedMarkersActionPerformed
        slideViewer.repaint();
    }//GEN-LAST:event_jCheckBoxShowSelectedMarkersActionPerformed

    private void jCheckBoxRegionHighlightActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxRegionHighlightActionPerformed
        slideViewer.repaint();
    }//GEN-LAST:event_jCheckBoxRegionHighlightActionPerformed

    private void jButtonExportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonExportActionPerformed
        jFileChooserAnnotationExport.setMultiSelectionEnabled(false);
        jFileChooserAnnotationExport.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int result = jFileChooserAnnotationExport.showDialog(jPanel1, "Export");
        File file = jFileChooserAnnotationExport.getSelectedFile();
        if ((file == null) || ("".equals(file.getName())) || (result != JFileChooser.APPROVE_OPTION)) {
            return;
        }

        // Make sure file has xml extention
        String filePath = file.getAbsolutePath();
        int lastPoint = filePath.lastIndexOf(".");
        String ext = "";
        if (lastPoint > 0) {
            ext = filePath.substring(lastPoint);
        }
        if (!ext.equalsIgnoreCase(".xml")) {
            filePath += ".xml";
            file = new File(filePath);
        }

        ArrayList<Annotation> toExport = new ArrayList<>();
        for (Annotation anno : annoSet.getAnnotations()) {
            if (anno.selected) {
                toExport.add(anno);
            } else {
                Annotation tempAnno = null;
                for (ROI roi : anno.getRois()) {
                    if (roi.selected) {
                        if (tempAnno == null) {
                            tempAnno = Annotation.createDefault();
                            tempAnno.attributes = anno.attributes;
                            tempAnno.id = anno.id;
                            tempAnno.lineColor = anno.lineColor;
                            tempAnno.name = anno.name;
                            tempAnno.selected = anno.selected;
                            tempAnno.type = anno.type;
                            tempAnno.visible = anno.visible;
                            toExport.add(tempAnno);
                        }
                        tempAnno.add(roi);
                    }
                }
            }
        }
        String xml = AperioAnnotationXML_Writer.getXMLString(toExport);
        FileWriter fw;
        try {
            fw = new FileWriter(file);
            fw.write(xml);
            fw.close();
        } catch (IOException ex) {
            Logger.getLogger(ROI_Manager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_jButtonExportActionPerformed

    private void jButtonImportActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonImportActionPerformed
        jFileChooserAnnotationImport.setMultiSelectionEnabled(false);
        int result = jFileChooserAnnotationImport.showDialog(jPanel1, "Import");
        File file = jFileChooserAnnotationImport.getSelectedFile();
        if ((file == null) || (result != JFileChooser.APPROVE_OPTION)) {
            return;
        }
        AnnotationsXML_Parser parser = new AnnotationsXML_Parser();
        try {
            ArrayList<Annotation> annos = parser.parse(file);
            for (Annotation anno : annos) {
                annoSet.add(anno);
            }
        } catch (ParserConfigurationException | SAXException | IOException ex) {
            Logger.getLogger(ROI_Manager.class.getName()).log(Level.SEVERE, null, ex);
        }
        jXTreeTableROIs.updateUI();
    }//GEN-LAST:event_jButtonImportActionPerformed

    private void jButtonSaveROIsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSaveROIsActionPerformed
        saveAnnotations();
    }//GEN-LAST:event_jButtonSaveROIsActionPerformed

    private void jButtonAddAnnotationSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonAddAnnotationSetActionPerformed
        addNewAnnotation();
    }//GEN-LAST:event_jButtonAddAnnotationSetActionPerformed

    private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown

    }//GEN-LAST:event_formComponentShown

    private void jComboBoxStudyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jComboBoxStudyActionPerformed
        promptToSaveROIs();
        populateAnnotationSet();
    }//GEN-LAST:event_jComboBoxStudyActionPerformed

    private void jComboBoxStudyPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_jComboBoxStudyPopupMenuWillBecomeVisible
        populateStudiesComboBox();
    }//GEN-LAST:event_jComboBoxStudyPopupMenuWillBecomeVisible

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButtonAddAnnotationSet;
    private javax.swing.JButton jButtonExport;
    private javax.swing.JButton jButtonImport;
    private javax.swing.JButton jButtonSaveROIs;
    private javax.swing.JCheckBox jCheckBoxRegionHighlight;
    private javax.swing.JCheckBox jCheckBoxShowAllMarkers;
    private javax.swing.JCheckBox jCheckBoxShowSelectedMarkers;
    private javax.swing.JComboBox<StudySource> jComboBoxStudy;
    private javax.swing.JFileChooser jFileChooser1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanelAddRoi;
    private javax.swing.JScrollPane jScrollPane1;
    private avl.sv.shared.JXTreeTableROIs jXTreeTableROIs;
    // End of variables declaration//GEN-END:variables

}
